
package async

import (
	"syscall"
	"unsafe"
)

// Client (requesting) side parts of "big data" transfers.

func (exch *Exchange) ShareInStart(size int, arg syscall.Arg) (syscall.Errno, *syscall.MemArea) {
	var dst *byte
	var flags uint

	rc := async_share_in_start(exch.ptr, syscall.SizeT(size), arg, &flags, &dst)
	if rc != 0 {
		return syscall.Errno(rc), nil
	}

	return syscall.EOK, syscall.UnsafeCreateArea(unsafe.Pointer(dst), size, flags)
}

func (exch *Exchange) ShareOutStart(area *syscall.MemArea, flags uint) syscall.Errno {
	if exch.ptr == nil {
		return syscall.EINVAL
	}

	slice := area.Slice()

	srcarg := syscall.Arg(uintptr(unsafe.Pointer(&slice[0])))
	sizearg := syscall.Arg(area.Size())
	flagsarg := syscall.Arg(flags)

	if (flags &^ area.Flags()) != 0 {
		// Flags are not a subset.
		return syscall.EPERM
	}

	if (uint64(area.Size()) != uint64(sizearg)) {
		// Make sure the conversion doesn't overflow.
		// That should never happen here, but better safe than sorry.
		// TODO: Do similar checks in other places.
		return syscall.ERANGE
	}

	rc, _, _, _, _, _ := exch.Request(syscall.IPC_M_SHARE_OUT, srcarg, sizearg, flagsarg, 0, 0)

	return syscall.Errno(rc)
}

// IPC_M_DATA_READ(byte *dst, size_t len)
func (exch *Exchange) DataReadAsync(dst []byte) *Handle {
	dstarg := syscall.Arg(uintptr(unsafe.Pointer(&dst[0])))
	lenarg := syscall.Arg(len(dst))
	return exch.RequestAsync(syscall.IPC_M_DATA_READ, dstarg, lenarg, 0, 0, 0)
}

func (exch *Exchange) DataReadStart(dst []byte) (syscall.Errno, int) {
	if dst == nil {
		return syscall.EINVAL, 0
	}

	dstarg := syscall.Arg(uintptr(unsafe.Pointer(&dst[0])))
	lenarg := syscall.Arg(len(dst))

	rc, _, bytes, _, _, _ := exch.Request(syscall.IPC_M_DATA_READ, dstarg, lenarg, 0, 0, 0)
	return syscall.Errno(rc), int(bytes)
}

func (exch *Exchange) DataWriteStart(data []byte) syscall.Errno {
	rc := async_data_write_start(exch.ptr, &data[0], syscall.SizeT(len(data)))
	return syscall.Errno(rc)
}

// Just a little wrapper.
// TODO: Can be made more efficient.
func (exch *Exchange) SendString(str string) syscall.Errno {
	return exch.DataWriteStart([]byte(str))
}
